package util4;

import lombok.*;
import lombok.experimental.Accessors;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/***
 * @title ListToTree
 * @description <TODO description class purpose>
 * @author liyanzhen
 * @version 1.0.0
 * @create 2024/5/16 21:41
 **/
public class ListToTree {
    /**
     * @param list 列表
     * @param id 主键
     * @param parentId 父主键
     * @param getChildrenMethod 返回子节点
     * @param setChildrenMethod 设置子节点
     * @param childrenCollectionGenerator 子节点集合构造器
     * @return 树
     */
    public static <E,ID> List<E> listToTree(
            List<E> list,
            Function<E, ID> id,
            Function<E, ID> parentId,
            Function<E, Collection<E>> getChildrenMethod,
            BiConsumer<E, Collection<E>> setChildrenMethod,
            CollectionBuilder<Collection<E>,E> childrenCollectionGenerator) {
        TreeRule<E,Collection<E>> treeRule = new TreeRule<E,Collection<E>>() {
            @Override
            public Function<E, Collection<E>> getChildren() {
                return getChildrenMethod;
            }
            @Override
            public BiConsumer<E, Collection<E>> setChildren() {
                return setChildrenMethod;
            }

            @Override
            public CollectionBuilder<Collection<E>, E> setChildrenCollectionGenerator() {
                return childrenCollectionGenerator;
            }
        };
        return listToTree(list, id, parentId, treeRule);
    }
    /**
     * @param list 列表
     * @param id 主键
     * @param parentId 父主键
     * @param getChildren 返回子节点
     * @param setChildren 设置子节点
     * @return 树
     */
    public static <E,ID> List<E> listToTree(List<E> list,
                                            Function<E, ID> id,
                                            Function<E, ID> parentId,
                                            Function<E, Collection<E>> getChildren,
                                            BiConsumer<E, Collection<E>> setChildren){
        return listToTree(list, id, parentId, getChildren, setChildren, ArrayList::new);
    }

    /**
     * @param list 列表
     * @param id 主键
     * @param parentId 父主键
     * @param getChildren 返回子节点，子节点不能为空，否则抛出空指针异常
     * @return 树
     */
    public static <E,ID> List<E> listToTree(List<E> list,
                                            Function<E, ID> id,
                                            Function<E, ID> parentId,
                                            @NonNull Function<E, Collection<E>> getChildren){
        return listToTree(list, id, parentId, getChildren, null);
    }
    /**
     * @param list 列表
     * @param id 主键
     * @param parentId 父主键
     * @return 树
     */
    public static <E,ID> List<TreeNode<E,ID>> listToTreeNode(
            List<E> list,
            Function<E, ID> id,
            Function<E, ID> parentId){
        List<TreeNode<E, ID>> treeNodeList = list.stream().map(l -> {
            try {
                ID pid = parentId.apply(l);
                return new TreeNode<>(l, id.apply(l), pid, null);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }).collect(Collectors.toList());
        return listToTree(treeNodeList, TreeNode::getId ,TreeNode::getParentId , new DefultTreeRule<>());
    }

    public static <E,ID,C extends Collection<E>> List<E> listToTree(
            List<E> list,
            Function<E, ID> id,
            Function<E, ID> parentId,
            TreeRule<E,C> treeRule
    ){
        final Function<E, C> getChildren = treeRule.getChildren();
        BiConsumer<E, C> setChildren = treeRule.setChildren();
        if (getChildren == null||setChildren==null){
            throw new RuntimeException("You must tell the system how to set up your child elements：<== getChildren and setChildren cant be null!");
        }
        CollectionBuilder<C, E> childrenCollectionGenerator = treeRule.setChildrenCollectionGenerator();
        if (childrenCollectionGenerator == null){
            throw new RuntimeException("You need to tell the system the constructor created by your subset：<== childrenCollectionGenerator cant be null!");
        }
        Map<ID, E> map = list.stream().collect(Collectors.toMap(k-> {
            try {
                return id.apply(k);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }, v -> v));
        List<E> root = new ArrayList<>();
        list.forEach(l -> {
            try {
                ID pid = parentId.apply(l);
                if (pid != null) {
                    E parent = map.get(pid);
                    if (parent != null) {
                        C children = getChildren.apply(parent);
                        if (children == null) {
                            children = childrenCollectionGenerator.build();
                        }
                        children.add(l);
                        // 设置父Node
                        if (treeRule.setParent()!=null) {
                            BiConsumer<E, E> setParent = treeRule.setParent();
                            setParent.accept(l, parent);
                        }
                        // 设hasChildren
                        if (treeRule.setHasChildren()!=null){
                            BiConsumer<E, Boolean> setHasChildren = treeRule.setHasChildren();
                            setHasChildren.accept(parent, true);
                        }
                        setChildren.accept(parent, children);
                    }else {
                        root.add(l);
                    }
                }else {
                    root.add(l);
                }
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        });
        // 回填
        BiConsumer<E, String> setFullPath = treeRule.setFullPath();
        BiConsumer<E, Integer> setDepth = treeRule.setDepth();
        BiConsumer<E, Integer> setSize = treeRule.setSize();
        if (setFullPath != null || setDepth != null || setSize != null){
            for (E entity : root) {
                ID parentID = parentId.apply(entity);
                String fullPath = "";
                if (parentID != null) {
                    fullPath += parentID;
                }
                setPathReturnDept(entity, 0, fullPath , id, treeRule);
            }
        }
        return root;
    }

    /**
     *
     * @param entity 实体对象
     * @param depth 深度
     * @param treeRule
     * @return size
     */
    public static <E,ID,C extends Collection<E>> Integer setPathReturnDept(E entity, int depth, String parentFullPath,Function<E, ID> id, TreeRule<E,C> treeRule){
        Integer size = 0;
        Collection<E> childrenList = treeRule.getChildren().apply(entity);
        if (treeRule.setFullPath()!=null){
            treeRule.setFullPath().accept(entity, parentFullPath);
        }
        if (treeRule.setDepth()!=null){
            treeRule.setDepth().accept(entity, depth);
        }
        if (childrenList == null|| childrenList.isEmpty()){
            if (treeRule.setSize()!=null){
                treeRule.setSize().accept(entity, size);
            }
        }else {
            for (E e : childrenList) {
                size++;
                Integer childSize = setPathReturnDept(e, depth+1, parentFullPath + treeRule.splitter() + id.apply(e), id, treeRule);
                size+=childSize;
            }
            if (treeRule.setSize()!=null){
                treeRule.setSize().accept(entity, size);
            }
        }
        return size;
    }
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class TreeNode<E,ID>{
        private E data;
        private ID id;
        private ID parentId;
        private Collection<TreeNode<E,ID>> children;
        private boolean hasChildren = false;
        private boolean hasParent = false;
        private TreeNode<E,ID> parent;
        private int depth;
        private int size = 0;
        private String fullPath = "";
        public TreeNode(E data, ID id, ID parentId){
            this.data = data;
            this.id = id;
            if (parentId!=null){
                this.parentId = parentId;
                this.hasParent = true;
            }else {
                this.depth = 0;
            }
        }

        protected TreeNode(E data, ID id, ID parentId, Collection<TreeNode<E, ID>> children) {
            this.data = data;
            this.id = id;
            this.children = children;
            if (parentId!=null){
                this.parentId = parentId;
                this.hasParent = true;
            }else {
                this.depth = 0;
            }
        }

        /**
         * 防止循环引用
         * @return
         */
        @Override
        public String toString() {
            return "TreeNode{" +
                    "data=" + data +
                    ", id=" + id +
                    ", parentId=" + parentId +
                    ", hasChildren=" + hasChildren +
                    ", hasParent=" + hasParent +
                    ", parent=" + parent +
                    ", depth=" + depth +
                    ", size=" + size +
                    ", fullPath='" + fullPath + '\'' +
                    '}';
        }
    }


    /***
     * @title ListToTree
     * @description You will write values to the method you implemented, and you can implement
     * corresponding methods according to your actual needs
     * @takeCare getChildren, setChildren, setChildrenCollectionGenerator is the core method that
     * must be implemented
     * @author liyanzhen
     * @version 1.0.0
     * @create 2024/5/19 04:56
     **/
    @Getter
    @Accessors(fluent = true)
    public static abstract class TreeRule<E,C extends Collection<E>>{
        public BiConsumer <E, String> setFullPath(){return null;}
        public BiConsumer <E, Integer> setDepth(){return null;}
        public BiConsumer <E, Integer> setSize(){return null;}
        public BiConsumer <E, E> setParent(){return null;}
        // getChildren和setChildren不能为空
        public abstract Function <E, C> getChildren();
        public abstract BiConsumer <E, C> setChildren();
        public BiConsumer <E, Boolean> setHasChildren(){return null;}
        public abstract CollectionBuilder<C,E> setChildrenCollectionGenerator();
        public String splitter(){return "/";}
    }

    public static class DefultTreeRule<E, ID> extends TreeRule<TreeNode<E, ID>,Collection<TreeNode<E, ID>>>{
        @Override
        public BiConsumer<TreeNode<E, ID>, String> setFullPath() {
            return TreeNode::setFullPath;
        }
        @Override
        public BiConsumer<TreeNode<E, ID>, Integer> setDepth() {
            return TreeNode::setDepth;
        }
        @Override
        public BiConsumer<TreeNode<E, ID>, Integer> setSize() {
            return TreeNode::setSize;
        }
        @Override
        public BiConsumer<TreeNode<E, ID>, TreeNode<E, ID>> setParent() {
            return TreeNode::setParent;
        }
        @Override
        public Function<TreeNode<E, ID>, Collection<TreeNode<E, ID>>> getChildren() {
            return TreeNode::getChildren;
        }
        @Override
        public BiConsumer<TreeNode<E, ID>, Collection<TreeNode<E, ID>>> setChildren() {
            return TreeNode::setChildren;
        }
        @Override
        public BiConsumer<TreeNode<E, ID>, Boolean> setHasChildren() {
            return TreeNode::setHasChildren;
        }

        @Override
        public CollectionBuilder<Collection<TreeNode<E, ID>>, TreeNode<E, ID>> setChildrenCollectionGenerator() {
            return ArrayList::new;
        }

        @Override
        public String splitter() {
            return super.splitter();
        }
    }

    public interface CollectionBuilder<C extends Collection<E>,E>{
        C build();
    }
}
